/*
 * MIT License
 *
 * Copyright (c) 2023 芊芊墨客
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 */

package com.zhangmeng.tools.controller;

import com.alibaba.druid.pool.DruidDataSource;
import com.zhangmeng.tools.dto.BeanField;
import com.zhangmeng.tools.dto.GenerateDetail;
import com.zhangmeng.tools.dto.GenerateInput;
import com.zhangmeng.tools.utils.AlertUtils;
import com.zhangmeng.tools.utils.TemplateUtil;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.scene.control.*;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.control.cell.TextFieldListCell;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.util.Callback;
import lombok.extern.slf4j.Slf4j;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.util.CollectionUtils;

import javax.sql.DataSource;
import java.math.BigDecimal;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;

/**
 * @author : 芊芊墨客
 * @version : 1.0
 * @date : 2023-02-21 16:34
 */
@Slf4j
public class MySQLCodeGenController {

    private JdbcTemplate jdbcTemplate;

    /**
     * mysql类型与java类型部分对应关系
     */
    private static Map<String, String> map = new HashMap<>();

    static {
        map.put("int", Integer.class.getSimpleName());
        map.put("tinyint", Integer.class.getSimpleName());
        map.put("double", Double.class.getSimpleName());
        map.put("float", Float.class.getSimpleName());
        map.put("decimal", BigDecimal.class.getSimpleName());
        map.put("date", Date.class.getSimpleName());
        map.put("timestamp", Date.class.getSimpleName());
        map.put("datetime", Date.class.getSimpleName());
        map.put("varchar", String.class.getSimpleName());
        map.put("text", String.class.getSimpleName());
        map.put("longtext", String.class.getSimpleName());
        map.put("bit", Boolean.class.getSimpleName());
        map.put("bigint", Long.class.getSimpleName());

    }

    @FXML
    private TextField table_name;

    @FXML
    public void preview_code() {
        String text = data_base.getText();
        if (text.length() == 0) {
            AlertUtils.alert_warning("请输入数据库名!");
            return;
        }

        String table = table_name.getText();
        if (table.length() == 0) {
            AlertUtils.alert_warning("请输入表名!");
            return;
        }
        GenerateDetail generateDetail = generateByTableName(table);
        log.info("generateDetail:{}", generateDetail);
        assignment_to_filed(generateDetail);
        bean_info.set(generateDetail);
    }

    @FXML
    private TextField path_field;

    @FXML
    private TextField bean_class_name_field;

    @FXML
    private TextField bean_package_name_field;

    @FXML
    private TextField dao_clas_name_field;

    @FXML
    private TextField dao_package_name_field;

    @FXML
    private TextField controller_class_name_field;

    @FXML
    private TextField controller_package_name_field;

    @FXML
    private TableColumn<BeanField, String> column_name;

    @FXML
    private TableColumn<BeanField, String> column_type;

    @FXML
    private TableColumn<BeanField, String> bean_field_name;

    @FXML
    private TableColumn<BeanField, String> bean_field_type;

    @FXML
    private TableColumn<BeanField, String> bean_field_value;

    @FXML
    private TableView<BeanField> tableView;

    public ObservableList<BeanField> list = FXCollections.observableArrayList();

    @FXML
    private TextField username;

    @FXML
    private TextField password;

    @FXML
    private TextField data_base;

    @FXML
    private TextField ip_address;

    @FXML
    private TextField port;

    @FXML
    private ComboBox<String> comboBox_table;

    @FXML
    private ComboBox<String> comboBox_data_base;

    private SimpleObjectProperty<GenerateDetail> bean_info = new SimpleObjectProperty<>();

    private ObservableList<String> table_list = FXCollections.observableArrayList();
    private ObservableList<String> data_base_list = FXCollections.observableArrayList();

    public static final String path = "d:/generate";
    public static final String bean_package_name = "com.dashidao.server.model";
    public static final String dao_package_name = "com.dashidao.server.dao";
    public static final String controller_package_name = "com.dashidao.server.controller";

    @FXML
    public void initialize() {
        username.setText("root");
        password.setText("root");
        ip_address.setText("localhost");
        port.setText("3306");
        tableView.setFixedCellSize(30);
        tableView.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);
        tableView.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
            if (newValue != null) {
                System.out.println(newValue.getName());
            }
        });
        column_name.setCellFactory(TextFieldTableCell.forTableColumn());
        column_name.setCellValueFactory(new PropertyValueFactory<>("columnName"));

        column_type.setCellFactory(TextFieldTableCell.forTableColumn());
        column_type.setCellValueFactory(new PropertyValueFactory<>("columnType"));

        bean_field_name.setCellFactory(TextFieldTableCell.forTableColumn());
        bean_field_name.setCellValueFactory(new PropertyValueFactory<>("name"));

        bean_field_type.setCellFactory(TextFieldTableCell.forTableColumn());
        bean_field_type.setCellValueFactory(new PropertyValueFactory<>("type"));

        bean_field_value.setCellFactory(TextFieldTableCell.forTableColumn());
        bean_field_value.setCellValueFactory(new PropertyValueFactory<>("columnDefault"));
        tableView.setItems(list);

        double width = tableView.getPrefWidth() / tableView.getColumns().size() + 100;
        column_name.setPrefWidth(width);
        column_type.setPrefWidth(width);
        bean_field_name.setPrefWidth(width);
        bean_field_type.setPrefWidth(width);
        bean_field_value.setPrefWidth(width);

        comboBox_table.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
            if (newValue != null) {
                log.info("选择的表名为:{}", newValue);
                table_name.setText(newValue);
            }
        });

        comboBox_data_base.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
            if (newValue != null) {
                log.info("选择的数据库名为:{}", newValue);
                data_base.setText(newValue);
                update_select_data_base();
            }
        });
    }

    private void update_select_data_base() {
        String text = data_base.getText();
        getJdbcTemplate();
        log.info("update_select_data_base:{}", text);
        this.jdbcTemplate.execute("use `" + text + "`");
    }

    @FXML
    public void get_all_data_base() {
        log.info("获取所有数据表.....");
        data_base_list.clear();
        getJdbcTemplate();
        List<Map<String, Object>> maps = this.jdbcTemplate.queryForList("show databases");
        for (Map<String, Object> map : maps) {
            log.info("map->{}", map.toString());
            data_base_list.add(map.get("Database").toString());
        }
        comboBox_data_base.setItems(data_base_list);
        comboBox_data_base.getSelectionModel().select(0);
        data_base.setText(comboBox_data_base.getSelectionModel().getSelectedItem());
    }

    @FXML
    public void save_code() {
        log.info("代码生成保存!");
        GenerateDetail generateDetail = bean_info.getValue();
        if (generateDetail == null) {
            AlertUtils.alert_warning("请预览之后在进行操作!");
            return;
        }
//        assignment_to_filed(generateDetail);
        GenerateInput generateInput = new GenerateInput();
        generateInput.setTableName(generateDetail.getTableName());
        generateInput.setPath(path_field.getText());

        generateInput.setControllerName(controller_class_name_field.getText());
        generateInput.setDaoName(dao_clas_name_field.getText());
        generateInput.setBeanName(bean_class_name_field.getText());

        generateInput.setDaoPackageName(dao_package_name_field.getText());
        generateInput.setControllerPkgName(controller_package_name_field.getText());
        generateInput.setBeanPackageName(bean_package_name_field.getText());

        List<BeanField> fields = generateDetail.getFields();
        List<String> beanFieldName = new ArrayList<>();
        List<String> beanFieldType = new ArrayList<>();
        List<String> beanFieldValue = new ArrayList<>();
        List<String> columnNames = new ArrayList<>();
        for (BeanField field : fields) {
            beanFieldName.add(field.getName());
            beanFieldType.add(field.getType());
            beanFieldValue.add(field.getColumnDefault());
            columnNames.add(field.getColumnName());
        }

        generateInput.setBeanFieldName(beanFieldName);
        generateInput.setBeanFieldType(beanFieldType);
        generateInput.setBeanFieldValue(beanFieldValue);
        generateInput.setColumnNames(columnNames);

        TemplateUtil.saveJava(generateInput);
        TemplateUtil.saveJavaDao(generateInput);
        TemplateUtil.saveController(generateInput);
        log.info("生成成功!");
        //清空
        bean_info.setValue(null);
    }

    @FXML
    public void fetch_table() {
        table_list.clear();
        getJdbcTemplate();
        List<Map<String, Object>> maps = this.jdbcTemplate.queryForList("SELECT table_name, table_type, TABLE_COMMENT FROM information_schema.tables WHERE table_schema =  (select database()) ORDER BY table_name DESC");
        for (Map<String, Object> map : maps) {
            String table_name = (String) map.get("table_name");
            log.info("table_name:{}", table_name);
            table_list.add(table_name);
        }
        comboBox_table.setItems(table_list);
        comboBox_table.getSelectionModel().select(0);
    }

    private void assignment_to_filed(GenerateDetail generateDetail) {
        path_field.setText(path);
        String bean_class_name = generateDetail.getBeanName();
        bean_class_name_field.setText(bean_class_name);
        bean_package_name_field.setText(bean_package_name);
        String dao_clas_name = bean_class_name + "Dao";
        dao_clas_name_field.setText(dao_clas_name);
        dao_package_name_field.setText(dao_package_name);
        String controller_class_name = bean_class_name + "Controller";
        controller_class_name_field.setText(controller_class_name);
        controller_package_name_field.setText(controller_package_name);

        list.clear();
        List<BeanField> fields = generateDetail.getFields();
        list.addAll(fields);
    }

    public JdbcTemplate getJdbcTemplate() {
        if (jdbcTemplate == null) {
            jdbcTemplate = new JdbcTemplate();

            DruidDataSource datasource = new DruidDataSource();
            String url = "jdbc:mysql://" + ip_address.getText() + ":" + port.getText() + "/" + data_base.getText() + "?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8&nullCatalogMeansCurrent=true";
            String driverClassName = "com.mysql.jdbc.Driver";
            datasource.setUrl(url);
            datasource.setUsername(username.getText());
            datasource.setPassword(password.getText());
            datasource.setDriverClassName(driverClassName);

            jdbcTemplate.setDataSource(datasource);
        }
        return jdbcTemplate;
    }

    private RowMapper<BeanField> beanFieldMapper = (rs, paramInt) -> {
        BeanField beanField = new BeanField();
        beanField.setColumnName(rs.getString("column_name"));
        beanField.setColumnType(rs.getString("data_type"));
        beanField.setColumnComment(rs.getString("column_comment"));
        beanField.setColumnDefault(rs.getString("column_default"));
        return beanField;
    };

    public GenerateDetail generateByTableName(String tableName) {
        GenerateDetail detail = new GenerateDetail();
        detail.setBeanName(upperFirstChar(tableName));
        List<BeanField> fields = listBeanField(tableName);
        detail.setFields(fields);
        detail.setTableName(tableName);
        return detail;
    }

    public List<BeanField> listBeanField(String tableName) {
        getJdbcTemplate();
        List<BeanField> beanFields = jdbcTemplate.query("select column_name, data_type, column_comment, column_default FROM information_schema.columns WHERE table_name= ? and table_schema = (select database())", new String[]{tableName}, beanFieldMapper);
        if (CollectionUtils.isEmpty(beanFields)) {
            AlertUtils.alert_warning("表" + tableName + "不存在");
            throw new IllegalArgumentException("表" + tableName + "不存在");
        }

        beanFields.parallelStream().forEach(b -> {
            b.setName(TemplateUtil.str2hump(b.getColumnName()));
            String type = map.get(b.getColumnType());
            if (type == null) {
                type = String.class.getSimpleName();
            }
            b.setType(type);
            if ("id".equals(b.getName())) {
                b.setType(Long.class.getSimpleName());
            }

            b.setColumnDefault(b.getColumnDefault() == null ? "" : b.getColumnDefault());
        });

        return beanFields;
    }

    public String upperFirstChar(String string) {
        String name = TemplateUtil.str2hump(string);
        String firstChar = name.substring(0, 1);
        name = name.replaceFirst(firstChar, firstChar.toUpperCase());
        return name;
    }
}
